#ifndef _NSA_NPCP_H
#define _NSA_NPCP_H
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/list.h>

#define NPCP_RX_RING_SIZE	4096
#define NPCP_TX_RING_SIZE	4096

#define RX_WINDOW_SIZE	128
#define TX_WINDOW_SIZE	64

#define NPCP_FRMAE_SIZE	2048	/* typical ethernet controller can 
								 * support 1522+ size by jumbo
								 */

#define NPCP_BLOCKING_SLEEP_US		20		/* block sleeping timer */
#define NPCP_BLOCKING_CYCLES		2		/* how many cycles can a single blocking 
											 * send wait loop
											 */

struct npcp_waiting_confirm{
#define NPCP_RC_SUCCESS_DO  0x0001
#define NPCP_RC_FAILED_DO   0x0002
    int confirm_cond;
    u_int32_t confirm_param;
    void (*confirm_handler)(int np_id, u_int32_t);
    char padding[4];
};
struct npcp_controller{

	/* instance key and basic information */
	u_int8_t host_addr[6]; 		/* local ethernet mac address, init when 
								 * ethX registered
								 */
	u_int8_t sci_addr[6];  		/* NSA sci macaddr, inited to broadcast */

	u_int16_t id;				/* system-wide unique ID of each nsa card */
    int *msg_enable;             /* debug message control */

	/* buffering and flow */

	spinlock_t seq_lock;		/* when checking seq_lock and doing flow control */
    spinlock_t ctrl_lock;   
    u_int16_t ctrl_seq;         /* last seq num in ctrl frame */
    u_int16_t rx_seq;			/* last seq num in rx frame */
	u_int16_t tx_seq;			/* last seq num filled in tx frame */

    u_int16_t tx_ed;			/* known ack-ed seq num by rx frame */
    u_int16_t last_confirmed;   /* last time npcp controller confirmed */
	unsigned long sending_state;	

    u_int16_t expected_seq;  	/* blocking sending can only be unblocked when 
								 * ack's host_tx equals to it, set before sending 
								 */

	struct npcp_waiting_confirm *tx_desc[NPCP_TX_RING_SIZE];

	u_int32_t np_tx_loss;
	u_int32_t host_tx_loss;

	u_int64_t rx_data;
    u_int64_t rx_ctrl;
    u_int64_t rx_flow;

    u_int64_t tx_data;
	u_int64_t tx_ctrl;
    u_int64_t tx_flow;

    u_int32_t rx_ctrl_timeout;
	u_int32_t rx_ctrl_ackerr;
    u_int32_t tx_busy;
    u_int32_t tx_locked;
    u_int32_t tx_reenterred;
    u_int32_t tx_skb_fail;
    u_int32_t tx_mem_fail;


//    struct work_struct confirm_task;

    /* lower layer */
    struct net_device *devX;

    struct sk_buff *buff; /* holding control(blocking) packet and its ack */

	/* upper layer , NEED REVISE LATER!!! */
	caddr_t np_dev;
    void (*np_dev_recv_data)(caddr_t np_dev, struct sk_buff *skb);

	int recv_data_ready;
};

#ifdef CONFIG_NPCP_END2END
typedef struct __npcp_header{
    u_int64_t smach:16, dmacl:16, dmach:32;
    u_int64_t rsv1:16, proto:16, smacl:32;
    u_int64_t rsv2:41, len:11, pkttype:4, type:8;
    u_int64_t rsv9:32, np_tx:16, host_tx:16;
}_npcp_header ;
#else  /* It's p2p NPCP header */
typedef struct __npcp_header{
    u_int64_t cache:8, rsv5:12, pkttype:4, type:8, qospri:4, rsv4:2, qodcolor:2,
        epif:3, rsv3:1, ipif:3, rsv2:1, len:11, rsv1:1, cmd:4;
    u_int64_t qosid:12, rsv12:4, zone:10, zval:1, limited:1, laid:3, laval:1,
        ilif:12, rsv11:4, elif:12, rsv10:4;
    u_int64_t acl_id:16, rsv8:8, cause_id:8, ssn_id:24, rsv7:4, 
            acl_avl:1, ssn_avl:1, rsv6:2;
   u_int64_t rsv9:32, np_tx:16, host_tx:16;
}_npcp_header ;
#endif /* CONFIG_NPCP_END2END */
/* agreement between host and NSA on NPCP */
#define NPCP_SENDING_BLOCK			0x0001  /* send routines should not be re-enterrable */
#define NPCP_SENDING_ACKED			0x0002  
#define NPCP_SENDING_ACKED_ERR		0x0004

#define NPCP_DATA			0x10	/* fast path packet over NPCP, ether and SSN*/
#define NPCP_CTL			0x20	/* NSA control packet mask*/
#define NPCP_CTL_REG_REQ	0x22	/* NSA register request */	
#define NPCP_CTL_REG_ACK	0x23	/* NSA register ack */
#define NPCP_CTL_ADV		0x24	/* NSA advertisement packet, from host */
#define NPCP_CTL_ADV_ACK	0x25	/* NSA adv. ack packet, by NSA echo to adv. */
#define NPCP_FLOW			0x80	/* NPCP flow control mask */
#define NPCP_FLOW_NACK		0x81	/* NPCP flow control, error remote tx_seq */
#define NPCP_FLOW_SACK		0x83	/* NPCP flow control, sync remote tx_seq */
#define NPCP_FLOW_RREQ		0x82	/* NPCP flow control, request remote resource */
#define NPCP_FLOW_RACK		0x85	/* NPCP flow control, resource reply from remote */
#define NPCP_ACK_MASK		0x01	
#define NPCP_TYPE_MASK		0xf0

#define NPCP_CTL_PACKET_LEN      2048
#define NPCP_DATA_PACKET_LEN     (1522+32+16)

#define ETH_NPCP_HEADER_SIZE	(sizeof( _npcp_header)+sizeof(struct ethhdr))
#define NPCP_HEADER_SIZE		sizeof( _npcp_header)
#define NPCP_MIN_SIZE           (64-NPCP_HEADER_SIZE)

#define npcp_type(skb) (( _npcp_header*)((skb)->data))->type
#define npcp_host_tx(skb) (( _npcp_header*)((skb)->data))->host_tx
#define npcp_np_tx(skb) (( _npcp_header*)((skb)->data))->np_tx
#define npcp_len(skb)   (( _npcp_header*)((skb)->data))->len

#define npcp_type_is_flow(type)	(type & NPCP_FLOW)
#define npcp_type_is_ctl(type)	(type & NPCP_CTL)
#define npcp_type_is_data(type)	(type & NPCP_DATA)
#define npcp_type_mask(type)	(type & NPCP_TYPE_MASK)

#ifdef CONFIG_NPCP_END2END
#define npcp_smac_set(skb, smac)  do { \
    u_int16_t _smach;   \
    u_int32_t _smacl;   \
    memcpy(&_smach, smac, 2);    \
    memcpy(&_smacl, smac+2, 4);    \
    (( _npcp_header*)((skb)->data))->smach=htons(_smach);\
    (( _npcp_header*)((skb)->data))->smacl=htons(_smacl);\
}while(0)

#define npcp_smac_get(skb, smac) do { \
    u_int16_t _smach;   \
    u_int32_t _smacl;   \
    _smach = (( _npcp_header*)((skb)->data))->smach; \
    _smacl = (( _npcp_header*)((skb)->data))->smacl; \
    _smach = htons(_smach);
    _smacl = htonl(_smacl);
    memcpy(smac, &_smach, 2);    \
    memcpy(smac+2, &_smacl, 4);    \
}while(0)

#define npcp_dmac_set(skb, dmac)  do { \
    u_int32_t _dmach;   \
    u_int16_t _dmacl;   \
    memcpy(&_dmach, dmac, 4);    \
    memcpy(&_dmacl, dmac+4, 2);    \
    (( _npcp_header*)((skb)->data))->dmach=htons(_dmach);\
    (( _npcp_header*)((skb)->data))->dmacl=htons(_dmacl);\
}while(0)

#define npcp_dmac_get(skb, dmac) do { \
    u_int32_t _dmach;   \
    u_int16_t _dmacl;   \
    _dmach = (( _npcp_header*)((skb)->data))->dmach; \
    _dmacl = (( _npcp_header*)((skb)->data))->dmacl; \
    _dmach = htonl(_dmach);
    _dmacl = htons(_dmacl);
    memcpy(dmac, &_smach, 4);    \
    memcpy(dmac+4, &_smacl, 2);    \
}while(0)

#define npcp_proto(skb) (( _npcp_header*)((skb)->data))->proto
#endif /* CONFIG_NPCP_END2END */

#define _NREG_NO        0x00000000U   /* 0b0000---- NOP */
#define _NREG_R         0x80000000U   /* 0b1000---- read (of course, wait) */
#define _NREG_W         0x20000000U   /* 0b0010---- write */
#define _NREG_WW        0x40000000U   /* 0b0100---- write and wait */
#define _NREG_WL        0x50000000U   /* 0b0101---- write and long wait */

#define _NRET_OK        0x08000000U   /* 0b----1000 result OK */
#define _NRET_ERR       0x02000000U   /* 0b----0010 read error */
#define _NRET_TIMEOUT   0x02000000U   /* 0b----0010 write timeout */

/* called by nsa_dev */

#define NPCP_SEND_OK                NETDEV_TX_OK
#define NPCP_SEND_ACK_TIMEOUT       -1
#define NPCP_SEND_ACK_ERR           -2
#define NPCP_SEND_BUSY              -3
#define NPCP_SEND_REENTERRED        -4

#define NPCP_RECV_OK		        0
#define NPCP_PROCESS_LOCAL	        1
#define NPCP_OUTOF_ORDER	        -1
#define NPCP_CTL_ACK_ERR	        -2


#define npcp_reg_write(skb, idx, REG, _data) \
   do{    \
    ((u_int32_t*)(skb->data))[(idx)<<1]= (htonl(REG|_NREG_W)); \
       ((u_int32_t*)(skb->data))[((idx)<<1)+1] = htonl(_data); \
        skb->len += 8; \
        idx++; \
   }while(0)

#define npcp_reg_write_wait(skb, idx, REG, _data) \
    do{   \
     ((u_int32_t*)(skb->data))[(idx)<<1]= (htonl(REG|_NREG_WW)); \
       ((u_int32_t*)(skb->data))[((idx)<<1)+1] = htonl(_data); \
        skb->len += 8; \
        idx++; \
    }while(0)

#define npcp_reg_write_wait_long(skb, idx, REG, _data) \
    do{   \
     ((u_int32_t*)(skb->data))[(idx)<<1]= (htonl(REG|_NREG_WL)); \
       ((u_int32_t*)(skb->data))[((idx)<<1)+1] = htonl(_data); \
        skb->len += 8; \
        idx++; \
    }while(0)

#define npcp_reg_read(skb, idx, REG)     \
    do{   \
     ((u_int32_t*)(skb->data))[(idx)<<1]= (htonl(REG|_NREG_R)); \
      skb->len += 8; \
      idx++; \
    }while(0)

#define npcp_reg_result_ok(skb, idx) \
    (skb && (((((u_int32_t*)(skb->data))[(idx)<<1])&htonl(_NRET_OK)) ==htonl(_NRET_OK)))

#define npcp_reg_data(skb, idx)   \
    htonl(((u_int32_t*)(skb->data))[((idx)<<1)+1])


#define DECLARE_SEND(ret, skb, idx) \
    struct sk_buff *skb; \
    int ret=0; \
    int idx=0;

#define START_SEND(skb) do {\
    skb = netdev_alloc_skb(NULL, NPCP_CTL_PACKET_LEN); \
    if(!skb){ \
        printk(KERN_ERR "%s: unabler to allocate skb\n", __FUNCTION__); \
        return -ENOMEM; \
    }\
    skb_reserve(skb, NPCP_HEADER_SIZE);\
    memset(skb->data, 0, 64); \
    }while(0)

#define DO_SEND(npcp, skb, wait) do {\
    int _ret;\
    struct timeval tv1, tv2; \
    if(skb->len < NPCP_MIN_SIZE) { \
        skb->len = NPCP_MIN_SIZE; \
    }\
    do_gettimeofday(&tv1); \
    if((_ret=npcp_send_control(npcp, NPCP_CTL_REG_REQ, &skb, wait))<0) {\
        printk(KERN_ERR "%s: error %d to send reg request\n", __FUNCTION__, _ret); \
        return _ret;\
    }\
    do_gettimeofday(&tv2); \
    /*ndebug("macro time test %lu\n", (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec));*/\
    }while(0)

#define ERROR_SEND(skb) do {\
    printk(KERN_ERR "%s: reg req result error\n", __FUNCTION__);\
    dev_kfree_skb(skb); \
    return -EINVAL; \
    }while(0)

#define END_SEND(skb) dev_kfree_skb(skb);return ret

#define incr_modulo(x,n) if (unlikely((x)+1 >= (n))) (x) = 0; else (x) = (x)+1;

#define npcp_npdev_probed(npcp)     { npcp->recv_data_ready = 1;}
#define npcp_npdev_unprobed(npcp)    { npcp->recv_data_ready = 0;}


#endif /* _NSA_NPCP_H*/
